#include "parsingdialog.h"
#include "ui_parsingdialog.h"

ParsingDialog::ParsingDialog(QWidget *parent)
    : QDialog(parent)

    , _quantnum(5)
    , _quantrange(0)
    , _offsetToPerappData(2)

    , ui(new Ui::ParsingDialog)
{
	ui->setupUi(this);
}

ParsingDialog::~ParsingDialog()
{
	delete ui;
}

void ParsingDialog::init(const QString &filename) {
	QFile file(filename);
	if (!file.open(QIODevice::ReadOnly)) {
		qDebug() << "Failed to open file" << filename;
		return;
	}
    
    _lines.clear();

	ui->selectedFilename->setText(filename);
    ui->selectedFilename->adjustSize();

	_outfilename = filename;
	_outfilename.append(".PARSED.csv");
	
	char linebuf[524288]; // 512 KB line buffer
	while (!file.atEnd()) {
		qint64 linelength = file.readLine(linebuf, 65536);
		if (linelength != -1) {
			
			if (linebuf[0] == '#') {
				qDebug() << "Skip Cmt:" << linebuf;
				continue;
			}
			
			QString linestr(linebuf);
			linestr = linestr.trimmed();
			linestr = linestr.remove(QRegExp("\\s+"));

			QStringList strlist = linestr.split(',', QString::KeepEmptyParts);
			
			_lines.push_back( strlist );
		}
		else {
			qDebug() << "ParsingDialog::init() : error while reading a line";
		}
	}
	
	file.close();

	qDebug() << "ParsingDialog::init() :" << _lines.size() << "data lines read";
}

void ParsingDialog::on_btn_parse_clicked()
{
	//
	// File and textstream
	//
	Q_ASSERT(!_outfilename.isNull());
	QFile file(_outfilename);
	if(!file.open(QIODevice::WriteOnly)) {
		qDebug() << "failed to open file" << _outfilename;
		return;
	}
	else {
		qDebug() << "will write to" << _outfilename;
	}

//    m_parseData_PriorityAndWinsize(file);
    m_parseData_Static_PriorityAndQualities(file);

	file.close();
}

void ParsingDialog::m_parseData_Static_PriorityAndQualities(QFile &file) {
    // to write a header line
//    static bool isFirst = true;

	QTextStream textout(&file);

//    QStringList sl = _lines.at(1);
    /*
    if (isFirst) {
		textout << "# apps,% overlapped\n";
        textout << sl.at(0) << "," << sl.at(1) << "\n\n";
		isFirst = false;
	}
    */

    if (ui->cb_priority->isChecked()) {
        m_parseSpecificItem(&textout, "Priority", 1);
        qDebug() << "Priority parsed";
    }

    if (ui->cb_currBW->isChecked()) {
        m_parseSpecificItem(&textout, "Current BW", 2);
        qDebug() << "Current BW parsed";
    }

    if (ui->cb_reqBW->isChecked()) {
        m_parseSpecificItem(&textout, "Required BW", 3);
        qDebug() << "Required BW parsed";
    }

    if (ui->cb_DQ->isChecked()) {
        m_parseSpecificItem(&textout, "Demanded Quality", 4);
        qDebug() << "Demanded Quality parsed";
    }

    if (ui->cb_OQ->isChecked()) {
        m_parseSpecificItem(&textout, "Observed Quality");
        qDebug() << "Observed Quality parsed";
    }

    if (ui->cb_OQ_DQ->isChecked()) {
        m_parseSpecificItem(&textout, "Observed Quality(Demanded)");
        qDebug() << "Observed Quality(Demanded) parsed";
    }

    textout.flush();
}


void ParsingDialog::m_parseSpecificItem(QTextStream *textout, const QString &dataitemname, int column) {

    //
    // Important NOTE !!
    // This function assumes a static wall layout during entire time !
    // The number of app, an app's priority data are NOT changing over time !
    //
    QStringList sl = _lines.at(1); // the first line is comment
    int numapp = sl.at(0).toInt();

    (*textout) << "\n" << dataitemname << "\n";

    //
    // Write GIDs at the first row
    //
    for(int i=_offsetToPerappData; i<sl.size(); i++) {
        QString perAppData = sl.at( i );
        if (perAppData.isEmpty()) {
            (*textout) <<  ",";
        }
        else {
            QStringList theitems = perAppData.split('|', QString::SkipEmptyParts);

            Q_ASSERT(theitems.size() > 1);
            (*textout) << theitems.at(0); // fitst item is GID
        }

        if ( i == sl.size() - 1 ) {
            (*textout) << "\n";
        }
        else {
            (*textout) <<  ",";
        }
    }

     int count = 0;

    //
    // for each measurement (for each line) at time t
    //
    QList<QStringList>::const_iterator it;
	for (it=_lines.constBegin(); it!=_lines.constEnd(); it++) {

		QStringList strlist = (*it);

		if (strlist.at(0).startsWith('#', Qt::CaseInsensitive)) {
			continue;
		}

        numapp = strlist.at(0).toInt();
        if (numapp <= 0) {
            qDebug() << count << "This line has no app data";
            continue;
        }

        count++;

        //
        // extract data for each app in this line
        //
        for(int i=_offsetToPerappData; i<strlist.size(); i++) {
            bool ok = false;

            //
            // each line begins with
            // 0 # apps
            // 1 % overlapped
            // so the offset is 2
            //
            QString perAppData = strlist.at(  i  );

            if (perAppData.isEmpty()) {
                (*textout) <<  ",";
            }
            else {
                QStringList theitems = perAppData.split('|', QString::SkipEmptyParts);

                qreal data = 0;
                if (QString::compare(dataitemname, "Observed Quality", Qt::CaseInsensitive)==0) {
                    qreal currbw = theitems.at(2).toDouble();
                    qreal reqbw = theitems.at(3).toDouble();
                    if (reqbw>0)
                        data = currbw / reqbw;
                }
                else if (QString::compare(dataitemname, "Observed Quality(demanded)", Qt::CaseInsensitive)==0) {
                    qreal currbw = theitems.at(2).toDouble();
                    qreal reqbw = theitems.at(3).toDouble();
                    qreal dq = theitems.at(4).toDouble();
                    if (reqbw*dq>0)
                        data = currbw / (reqbw * dq);
                }
                else {
                    Q_ASSERT(theitems.size() > column);
                    data = theitems.at(column).toDouble(&ok);
                    if (!ok) {
                        qDebug() << "Failed to convert item to data";
                    }
                }

                (*textout) << data;
            }

            if ( i == strlist.size() - 1 ) {
                (*textout) << "\n";
            }
            else {
                (*textout) <<  ",";
            }
        }
    }

    textout->flush();
}






void ParsingDialog::findGlobalMinMaxPriority() {
	qreal min = 999999999999;
	qreal max = 0;

	//
	// for each line
	//
    QList<QStringList>::const_iterator it;
	for (it=_lines.constBegin(); it!=_lines.constEnd(); it++) {

		QStringList strlist = (*it);
		int numapp = strlist.at(0).toInt();

		//
		// extract priority values
		//
		for(int i=0; i<numapp; i++) {
			qreal priority = 0.0;
//			int winsize = 0;

			QString strpair = strlist.at(i + _offsetToPerappData);
			strpair = strpair.trimmed();
			QStringList data = strpair.split('|', QString::SkipEmptyParts);

			priority = data.at(0).toDouble();
//			winsize = data.at(1).toInt();

			min = qMin(priority, min);
			max = qMax(priority, max);
		}
	}
	_globalMinPriority = min;
	_globalMaxPriority = max;

    //
    // figure out the priority range an each quantized section covers
    //
	_quantrange = (max-min)/_quantnum;
	qDebug() << "ParsingDialog::findGlobalMinMaxPriority():" << "(" << max << "-" << min << ") /" << _quantnum << " = " << _quantrange;

}

int ParsingDialog::getQuantLevelFromPriority(qreal p) {
	Q_ASSERT(_quantrange > 0);
	Q_ASSERT(_quantnum > 0);

	int retval = 0;

	qreal i = _quantrange;
	int ret=0;

	while ( p >= i ) {
		i += _quantrange;
		ret++;
	}


	if (ret >= _quantnum) {
		retval =  _quantnum - 1;
	}
	else {
		retval = ret;
	}

	Q_ASSERT(retval >= 0);
	Q_ASSERT(retval < _quantnum);

	return retval;
}

void ParsingDialog::m_parseData_PriorityAndWinsize(QFile &file) {

    static bool isFirst = true;

	QTextStream textout(&file);



	if (isFirst) {
		textout << "count,# apps,% empty,% overlapped, WindSize Variance,Windsize avg.abs.deviation,lowest,low,medium,high,highest\n";
		isFirst = false;
	}

	//
	// get number of quantization
	//
//	_quantnum = ui->ledit_numQuant->text().toInt();
	int *appCount = (int *)malloc(sizeof(int) * _quantnum);



	findGlobalMinMaxPriority();



	int count = 0;

	//
	// for each line
	//
	// 0 # apps
	// 1 % overlapped
	//

    QList<QStringList>::const_iterator it;
	for (it=_lines.constBegin(); it!=_lines.constEnd(); it++) {
		count++;
		QStringList strlist = (*it);


        //		qreal minPriority = 9999999990.0;
        //		qreal maxPriority = 0.0;
        //		qreal pSum = 0.0; // of a data line
        //		qreal avgPriority = 0.0; // of a data line


        int numapp = strlist.at(0).toInt();
        if (numapp <= 0) {
            qDebug() << count << "This line has no app data";
            continue;
        }

//        qreal empty = strlist.at(2).toDouble() * 100;
        qreal overl = strlist.at(1).toDouble() * 100;
//        int avgWindowSize = strlist.at(4).toInt();
        int avgWindowSize = 0;



        textout << strlist.at(0) << "," //  count
                << numapp << "," // # apps
                << overl << ",";// % overlapped




        for (int i=0; i<_quantnum; i++) {
            appCount[i] = 0;
        }

        // sum of the squared deviation
        qreal winsizesum_SD = 0.0;

        // sum of the absolute deviation
        qreal winsizesum_AD = 0.0;

        // extract priority values
        for(int i=0; i<numapp; i++) {
            bool ok = false;

            QString strpair = strlist.at(i + _offsetToPerappData);
            QStringList theitems = strpair.split('|', QString::SkipEmptyParts);

            qreal p = theitems.at(0).toDouble(&ok);
            if (!ok) {
                qDebug() << "Can't convert str to double!!!!!";
                exit(1);
            }

            //			minPriority = qMin(p, minPriority);
            //			maxPriority = qMax(p, maxPriority);
            //			pSum += p;
            //
            // The number of apps belonging to a specific priority range
            //
            appCount[getQuantLevelFromPriority(p)]++;



            int winsize = theitems.at(1).toInt(&ok);
            if (!ok) {
                qDebug() << "Can't convert str to int!!!!!";
                exit(1);
            }

            //
            // Sum of squared deviation
            //
            winsizesum_SD = qPow( winsize - avgWindowSize , 2 );

            //
            // Sum of absolute deviation
            //
            winsizesum_AD = qAbs( winsize - avgWindowSize );
        }
        //		avgPriority = pSum / (qreal)numapp;

        //
        // variance of window size = the mean of the equally likely squared deviations
        //
        qreal winsizeVariance = winsizesum_SD / numapp;

        // write the variance
        textout << winsizeVariance << ",";

        //
        // Average absolute deviation
        //
        qreal winsizeAvgAbsDeviation = winsizesum_AD / numapp;

        textout << winsizeAvgAbsDeviation << ",";


        //
        // write priority data
        //
        for (int i=0; i<_quantnum; i++) {
            textout << appCount[i];

            if (i == _quantnum -1) {
                textout << "\n";
            }
            else {
                textout << ",";
            }
        }
        textout.flush();
    }

    free(appCount);
}
